﻿using System;
using Microsoft.Services.Clab.UI.Mvvmc.Properties;

namespace Microsoft.Services.Clab.UI.Mvvmc
{
    public abstract class ControllerFactoryBase : IControllerFactory
    {
        protected ControllerFactoryBase()
        {
        }

        protected abstract IController CreateControllerInstance(string logicalName, object initParameters);

        private void CreateControllerCore(string logicalName, object initParameters = null, IController parentController = null, Action<IController> onCreatingAction = null, Action<IController> onCreatedAction = null, Action<IController> onInitializedAction = null, Action<Exception> onException = null)
        {
            IController controller = null;
            try
            {
                if (onCreatingAction != null)
                {
                    onCreatingAction(controller);
                }

                controller = CreateControllerInstance(logicalName, initParameters);

                if (controller == null)
                {
                    throw new InvalidOperationException(Resources.CreateControllerInstanceImplementationCannotReturnNull);
                }

                controller.LogicalName = logicalName;
                if (onCreatedAction != null)
                {
                    onCreatedAction(controller);
                }

                controller.ExecuteWhen<IController>(
                    (propertyName, c) => propertyName == "State" && c.State == ControllerState.Initialized,
                    c =>
                    {
                        DispatcherProxy.BeginInvoke(() =>
                        {
                            if (onInitializedAction != null)
                            {
                                onInitializedAction(c);
                            }
                            c.DoStartupAction();
                        });
                    },
                    true);

                controller.Initialize(parentController, initParameters);
            }
            catch (Exception ex)
            {
                try
                {
                    if (controller != null)
                    {
                        controller.Dispose();
                    }
                }
                catch (Exception)
                {
                }

                var ex1 = new ControllerException(Resources.ExceptionInControllerFactory.FormatEx(logicalName), Guid.Empty, null, ex);
                if (onException != null)
                {
                    onException(ex1);
                }
                else if (DefaultExceptionHandler != null)
                {
                    DefaultExceptionHandler(ex1);
                }
                else
                {
                    throw ex1;
                }
            }
        }

        #region IControllerFactory Members

        public void CreateController(string logicalName, object initParameters = null, IController parentController = null, Action<IController> onCreatingAction = null, Action<IController> onCreatedAction = null, Action<IController> onInitializedAction = null, Action<Exception> onException = null)
        {
            DispatcherProxy.BeginInvoke(() =>
            {
                CreateControllerCore(logicalName, initParameters, parentController, onCreatingAction, onCreatedAction, onInitializedAction, onException);
            });
        }

        public void CreateControllerSync(string logicalName, object initParameters = null, IController parentController = null, Action<IController> onCreatingAction = null, Action<IController> onCreatedAction = null, Action<IController> onInitializedAction = null, Action<Exception> onException = null)
        {
            DispatcherProxy.Invoke(() =>
            {
                CreateControllerCore(logicalName, initParameters, parentController, onCreatingAction, onCreatedAction, onInitializedAction, onException);
            });
        }

        #endregion

        public static Action<Exception> DefaultExceptionHandler;
    }
}
